package com.ashlikun.wheelview_rv

import android.content.Context
import android.graphics.Camera
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.LinearGradient
import android.graphics.Matrix
import android.graphics.Paint
import android.graphics.PaintFlagsDrawFilter
import android.graphics.RectF
import android.graphics.Shader
import android.util.AttributeSet
import android.view.View
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.LinearSnapHelper
import androidx.recyclerview.widget.RecyclerView
import com.ashlikun.wheelview_rv.WheelUtils.radianToRadio
import kotlin.math.abs
import kotlin.math.cos
import kotlin.math.sin

/**
 * @author　　: 李坤
 * 创建时间: 2024/7/23 13:45
 * 邮箱　　：496546144@qq.com
 *
 * 功能介绍：用RecyclerView 实现滑轮
 */
open class WheelRecyclerView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : RecyclerView(context, attrs, defStyleAttr) {
    var is3D: Boolean = true
        set(value) {
            field = value
            invalidate()
        }

    //此参数影响左右旋转对齐时的效果,系数越大,越明显,自己体会......(0-1之间)
    var scale3d: Float = 0.75f
        set(value) {
            field = value
            invalidate()
        }

    //立体效果时候的对齐方式
    var gravity3D: Int = GRAVITY_CENTER
        set(value) {
            field = value
            invalidate()
        }

    //显示的item数量
    var offsetCount: Int = 3
        set(value) {
            field = value
            itemDegree = 180f / (value * 2 + 1)
            invalidate()
        }

    //每个item大小,  垂直布局时为item的高度, 水平布局时为item的宽度
    var itemSize: Int

    //布局方向
    var orientation: Int = VERTICAL
        set(value) {
            field = value
            invalidate()
        }

    //分割线颜色
    var dividerColor: Int = Color.BLACK
        set(value) {
            field = value
            dividerPaint.color = value
            invalidate()
        }

    //分割线之间距离 间距
    var dividerSpacing: Int = 0
        set(value) {
            field = value
            invalidate()
        }

    //分割线之间距离 间距
    var dividerSize: Int = 0
        set(value) {
            field = value
            dividerPaint.strokeWidth = value.toFloat()
            invalidate()
        }

    //是否绘制阴影
    var isShader: Boolean = false
    var shaderColor: Int = Color.WHITE
        set(value) {
            field = value
            firstShaderPaint.shader = null
            secondShaderPaint.shader = null
            invalidate()
        }

    //是否循环
    var isLoop: Boolean = false

    //自动滚动到中间，默认第一个
    var isAutoScrollCenter: Boolean = false


    //每个item平均下来后对应的旋转角度 , 根据中间分割线上下item和中间总数量计算每个item对应的旋转角度
    var itemDegree: Float = 0f
        private set


    //滑动轴的半径
    val wheelRadio: Float
        get() = radianToRadio(itemSize, itemDegree).toFloat()

    //自身中心点
    var centerX = 0f
        private set
    var centerY = 0f
        private set

    var onDrawLine: ((canvas: Canvas, first: RectF, second: RectF, paint: Paint) -> Unit)? = null
    var onSelected: ((index: Int) -> Unit)? = null
    var onScrolled: ((index: Int) -> Unit)? = null

    //3D旋转
    val camera by lazy { Camera() }
    private val cameraMatrix by lazy { Matrix() }

    //分割线画笔
    private val dividerPaint by lazy {
        Paint().also {
            it.isAntiAlias = true
            it.style = Paint.Style.STROKE
        }
    }
    private val layoutManager: LinearLayoutManager

    //渐变画笔
    private val firstShaderPaint by lazy { Paint().also { it.isAntiAlias = true } }
    private val secondShaderPaint by lazy { Paint().also { it.isAntiAlias = true } }
    private var wheelAdapter: WheelWrapAdapter? = null


    //选中的item位置,中间
    var currentItem = -1
        set(value) {
            field = value
            if (isSelfSetCurrentItem) {
                isSelfSetCurrentItem = false
            } else {
                setCurrentItem(value)
            }
        }
    private var isSelfSetCurrentItem = false

    init {
        val a = context.obtainStyledAttributes(attrs, R.styleable.WheelRecyclerView)
        is3D = a.getBoolean(R.styleable.WheelRecyclerView_wrv_is3d, is3D)
        scale3d = a.getFloat(R.styleable.WheelRecyclerView_wrv_3d_scale, scale3d)
        offsetCount = a.getInt(R.styleable.WheelRecyclerView_wrv_itemCount, offsetCount)
        dividerColor = a.getColor(R.styleable.WheelRecyclerView_wrv_dividerColor, dividerColor)
        itemSize = a.getDimensionPixelOffset(R.styleable.WheelRecyclerView_wrv_itemSize, WheelUtils.dip2px(context, 90f))
        dividerSize = a.getDimensionPixelOffset(R.styleable.WheelRecyclerView_wrv_dividerSize, WheelUtils.dip2px(context, 1f))
        dividerSpacing = a.getDimensionPixelOffset(R.styleable.WheelRecyclerView_wrv_dividerSpacing, itemSize)
        orientation = a.getInt(R.styleable.WheelRecyclerView_wrv_orientation, orientation)
        gravity3D = a.getInt(R.styleable.WheelRecyclerView_wrv_3dgravity, gravity3D)
        isShader = a.getBoolean(R.styleable.WheelRecyclerView_wrv_shader, isShader)
        shaderColor = a.getColor(R.styleable.WheelRecyclerView_wrv_shaderColor, shaderColor)
        isLoop = a.getBoolean(R.styleable.WheelRecyclerView_wrv_isLoop, isLoop)
        isAutoScrollCenter = a.getBoolean(R.styleable.WheelRecyclerView_wrv_isAutoScrollCenter, isAutoScrollCenter)
        val zSize = a.getFloat(R.styleable.WheelRecyclerView_wrv_zSize, 3f)
        a.recycle()
        overScrollMode = OVER_SCROLL_NEVER
        //让滑动结束时都能定到中心位置
        LinearSnapHelper().attachToRecyclerView(this)
        camera.setLocation(0f, 0f, (-8 * zSize))
        layoutManager = LinearLayoutManager(context)
        layoutManager.orientation = orientation
        setLayoutManager(layoutManager)
        addOnScrollListener(object : OnScrollListener() {
            private var lastItem = -1
            private var lastScroll = -1
            override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
                if (wheelAdapter == null) return
                val centerItemPosition = findCenterItemPosition()
                if (centerItemPosition == -1) return
                if (newState == SCROLL_STATE_IDLE) {
                    isSelfSetCurrentItem = true
                    currentItem = centerItemPosition
                    if (currentItem != lastItem) {
                        onSelected?.invoke(centerItemPosition)
                        lastItem = currentItem
                    }
                }

            }

            override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
                super.onScrolled(recyclerView, dx, dy)
                if (onScrolled != null) {
                    val centerItemPosition = findCenterItemPosition()
                    if (centerItemPosition != lastScroll) {
                        onScrolled?.invoke(centerItemPosition)
                        lastScroll = centerItemPosition
                    }
                }
            }
        })
        addOnItemTouchListener(OnItemClickListener(this, onClick = { viewHolder, e ->
            if (viewHolder.itemViewType != WheelWrapAdapter.EMPTY_TYPE) {
                onItemClick(viewHolder.adapterPosition)
            }
        }))
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        var widthSize = MeasureSpec.getSize(widthMeasureSpec)
        var heightSize = MeasureSpec.getSize(heightMeasureSpec)
        var widthMode = MeasureSpec.getMode(widthMeasureSpec)
        var heightMode = MeasureSpec.getMode(heightMeasureSpec)
        //水平布局时,最好固定高度,垂直布局时最好固定宽度
        if (orientation == HORIZONTAL) {
            heightSize = if (heightMode == MeasureSpec.EXACTLY) heightSize
            else itemSize + paddingTop + paddingBottom
            widthSize = itemSize * (offsetCount * 2 + 1) + paddingLeft + paddingRight
            super.onMeasure(MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(heightSize, MeasureSpec.EXACTLY))
        } else {
            widthSize = if (widthMode == MeasureSpec.EXACTLY) widthSize
            else itemSize + paddingLeft + paddingRight
            heightSize = itemSize * (offsetCount * 2 + 1) + paddingTop + paddingBottom
            super.onMeasure(MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(heightSize, MeasureSpec.EXACTLY))
        }

    }


    override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
        super.onLayout(changed, l, t, r, b)
        centerX = width / 2f
        centerY = height / 2f
    }

    override fun setAdapter(adapter: Adapter<*>?) {
        if (adapter == null) super.setAdapter(adapter)
        else {
            wheelAdapter = WheelWrapAdapter(this, adapter)
            super.setAdapter(wheelAdapter)
            if (adapter.itemCount > 0) {
                if (isAutoScrollCenter) scrollToPosition(wheelAdapter!!.getCenterPosition())
                else scrollToPosition(wheelAdapter!!.getFirstPosition())
                isSelfSetCurrentItem = true
                if (isAutoScrollCenter) {
                    currentItem = adapter.itemCount / 2
                } else currentItem = 0
                onSelected?.invoke(currentItem)
            }

        }
    }

    override fun drawChild(canvas: Canvas, child: View, drawingTime: Long): Boolean {
        if (!is3D) return super.drawChild(canvas, child, drawingTime)
        canvas.save()
        if (orientation == VERTICAL) {
            verticalCanvasForDrawChild(canvas, child, translateX(centerX))
        } else {
            horizontalCanvasForDrawChild(canvas, child)
        }
        val drawChild = super.drawChild(canvas, child, drawingTime)
        canvas.restore()
        return drawChild
    }

    /**
     * 根据对齐方式,计算出垂直布局时X轴移动的位置
     */
    private fun translateX(parentCenterX: Float): Float {
        when (gravity3D) {
            GRAVITY_LEFT -> return parentCenterX * (1 + scale3d)
            GRAVITY_RIGHT -> return parentCenterX * (1 - scale3d)
        }
        return parentCenterX
    }

    /**
     * 垂直方向旋转canvas
     */
    fun verticalCanvasForDrawChild(c: Canvas, child: View, translateX: Float) {
        val itemCenterY = (child.top + child.bottom) * 0.5f
        //当前view中心距离RV中心点的距离,上面负数，下面正数
        val scrollOffY = itemCenterY - centerY
        //垂直布局时要以X轴为中心旋转
        //旋转X的角度
        val rotateDegreeX = rotateLimitRightAngle(scrollOffY * itemDegree / itemSize)
        val rotateSinX = sin(Math.toRadians(rotateDegreeX.toDouble())).toFloat()
        //因旋转导致界面视角的偏移
        val rotateOffY = scrollOffY - wheelRadio * rotateSinX
        //Log.i("you", "drawVerticalItem degree " + rotateDegreeX);
        //因旋转导致界面视角的偏移
        c.translate(0.0f, -rotateOffY)
        camera.save()

        //旋转时离视角的z轴方向也会变化,先移动Z轴再旋转
        val z = (wheelRadio * (1 - abs(cos(Math.toRadians(rotateDegreeX.toDouble()))))).toFloat()
        camera.translate(0f, 0f, z)
        camera.rotateX(-rotateDegreeX)
        camera.getMatrix(cameraMatrix)
        camera.restore()
        cameraMatrix.preTranslate(-translateX, -itemCenterY)
        cameraMatrix.postTranslate(translateX, itemCenterY)
        c.concat(cameraMatrix)
    }

    /**
     * 水平方向旋转canvas
     */
    fun horizontalCanvasForDrawChild(c: Canvas, child: View) {
        val itemCenterX = (child.left + child.right) * 0.5f
        val scrollOffX = itemCenterX - centerX
        val rotateDegreeY = rotateLimitRightAngle(scrollOffX * itemDegree / itemSize) //垂直布局时要以Y轴为中心旋转
        val rotateSinY = sin(Math.toRadians(rotateDegreeY.toDouble())).toFloat()
        val rotateOffX = scrollOffX - wheelRadio * rotateSinY //因旋转导致界面视角的偏移
        //Log.i("you", "drawVerticalItem degree " + rotateDegreeY);
        c.translate(-rotateOffX, 0.0f)
        camera.save()
        val z = (wheelRadio * (1 - abs(cos(Math.toRadians(rotateDegreeY.toDouble()))))).toFloat()
        camera.translate(0f, 0f, z)
        camera.rotateY(rotateDegreeY)
        camera.getMatrix(cameraMatrix)
        camera.restore()
        cameraMatrix.preTranslate(-itemCenterX, -centerY)
        cameraMatrix.postTranslate(itemCenterX, centerY)
        c.concat(cameraMatrix)
    }

    /**
     * 旋转的角度绝对值不能大于90度
     */
    fun rotateLimitRightAngle(degree: Float): Float {
        if (degree >= RIGHTANGLE) {
            return RIGHTANGLE
        }
        return if (degree <= -RIGHTANGLE) {
            -RIGHTANGLE
        } else degree
    }

    /**
     * 获取中心点位置
     * @return 真实
     */
    fun findCenterItemPosition(): Int {
        if (adapter == null || centerY == 0f || centerX == 0f) return -1
        val centerView = findChildViewUnder(centerX, centerY)
        if (centerView != null) {
            val adapterPosition = getChildAdapterPosition(centerView)
            if (adapterPosition >= 0) {
                return wheelAdapter!!.getActualPosition(adapterPosition)
            }
        }
        return -1
    }

    /**
     * 在这里画分割线与遮罩层
     */
    override fun dispatchDraw(c: Canvas) {
        super.dispatchDraw(c)
        c.drawFilter = PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG or Paint.FILTER_BITMAP_FLAG)
        var firstRectF: RectF
        var secondRectF: RectF
        var shaderFirstRectF: RectF? = null
        var shaderSecondRectF: RectF? = null
        if (orientation == VERTICAL) {
            val dividerOff = (height - dividerSpacing) / 2.0f
            val firstY = paddingTop + dividerOff
            val secondY = height - paddingBottom - dividerOff
            firstRectF = RectF(paddingLeft.toFloat(), firstY, (width - paddingLeft - paddingRight).toFloat(), firstY)
            secondRectF = RectF(paddingLeft.toFloat(), secondY, (width - paddingLeft - paddingRight).toFloat(), secondY)
            //如果需要画渐变阴影
            if (isShader) {
                val rectTop = if (is3D) firstY - wheelRadio else 0f
                val rectBottom = if (is3D) secondY + wheelRadio else height.toFloat()
                if (firstShaderPaint.shader == null) firstShaderPaint.shader = LinearGradient(0f, firstY, 0f, rectTop, Color.TRANSPARENT, shaderColor, Shader.TileMode.MIRROR)
                if (secondShaderPaint.shader == null) secondShaderPaint.shader = LinearGradient(0f, secondY, 0f, rectBottom, Color.TRANSPARENT, shaderColor, Shader.TileMode.MIRROR)
                shaderFirstRectF = RectF(left.toFloat(), rectTop, right.toFloat(), firstY)
                shaderSecondRectF = RectF(left.toFloat(), secondY, right.toFloat(), rectBottom)
            }
        } else {
            val dividerOff = (width - dividerSpacing) / 2.0f
            val firstX = left + dividerOff
            val secondX = right - dividerOff
            firstRectF = RectF(firstX, top.toFloat(), firstX, bottom.toFloat())
            secondRectF = RectF(secondX, top.toFloat(), secondX, bottom.toFloat())

            //如果需要画渐变阴影
            if (isShader) {
                val rectLeft = if (is3D) firstX - wheelRadio else 0f
                val rectRight = if (is3D) secondX + wheelRadio else width.toFloat()
                if (firstShaderPaint.shader == null) firstShaderPaint.shader = LinearGradient(firstX, 0f, rectLeft, 0f, Color.TRANSPARENT, shaderColor, Shader.TileMode.MIRROR)
                if (secondShaderPaint.shader == null) secondShaderPaint.shader = LinearGradient(secondX, 0f, rectRight, 0f, Color.TRANSPARENT, shaderColor, Shader.TileMode.MIRROR)
                shaderFirstRectF = RectF(rectLeft, top.toFloat(), firstX, bottom.toFloat())
                shaderSecondRectF = RectF(secondX, top.toFloat(), rectRight, bottom.toFloat())
            }
        }
        if (onDrawLine != null) {
            onDrawLine?.invoke(c, firstRectF, secondRectF, dividerPaint)
        } else {
            c.drawLine(firstRectF.left, firstRectF.top, firstRectF.right, firstRectF.bottom, dividerPaint)
            c.drawLine(secondRectF.left, secondRectF.top, secondRectF.right, secondRectF.bottom, dividerPaint)
        }
        if (isShader && shaderSecondRectF != null && shaderFirstRectF != null) {
            c.drawRect(shaderFirstRectF, firstShaderPaint)
            c.drawRect(shaderSecondRectF, secondShaderPaint)
        }
    }

    /**
     * @param position 内部位置
     */
    fun onItemClick(position: Int, isSmooth: Boolean = true) {
//        setCurrentItem(getActualPosition(position), isSmooth)
//        return
        if (wheelAdapter != null && position >= 0) {
            val centerPosition = findCenterItemPosition()
            if (position != centerPosition) {
                val first = WheelUtils.findFirstVisibleItemPosition(layoutManager!!)
                val last = WheelUtils.findLastVisiblePosition(layoutManager!!)
                val center = (first + last) / 2
                if (position != center) {
                    var toPosition: Int
                    if (position < center) {
                        toPosition = position - offsetCount
                    } else {
                        toPosition = position + offsetCount
                        smoothScrollToPosition(position + offsetCount)
                    }
                    if (isSmooth) {
                        smoothScrollToPosition(toPosition)
                    } else {
                        scrollToPosition(toPosition)
                    }
                }
            }
        }
    }

    /**
     * @param position 外界认为的位置
     */
    fun setCurrentItem(position: Int, isSmooth: Boolean = true) {
        if (wheelAdapter != null && position >= 0 && layoutManager != null) {
            val centerPosition = findCenterItemPosition()
            if (position != centerPosition) {
                var toPosition: Int
                val first = WheelUtils.findFirstVisibleItemPosition(layoutManager!!)
                val last = WheelUtils.findLastVisiblePosition(layoutManager!!)
                val center = (first + last) / 2
                val neibuPosition = getFanActualPosition(position)
                if (neibuPosition < center) {
                    toPosition = neibuPosition - offsetCount
                } else {
                    toPosition = neibuPosition + offsetCount
                }
                if (isSmooth) {
                    smoothScrollToPosition(toPosition)
                } else {
                    scrollToPosition(toPosition)
                }
            }
        }
    }

    /**
     * 实际位置
     * @param position 内部位置
     * @return 外部位置
     */
    fun getActualPosition(position: Int) = wheelAdapter?.getActualPosition(position) ?: -1

    /**
     * 反转位置
     * @param position 外部位置
     * @return 内部位置
     */
    fun getFanActualPosition(position: Int) = wheelAdapter?.getFanActualPosition(position) ?: -1

    companion object {
        const val RIGHTANGLE = 90f

        /**
         * 垂直布局时的靠左,居中,靠右立体效果
         */
        const val GRAVITY_LEFT = 1
        const val GRAVITY_CENTER = 2
        const val GRAVITY_RIGHT = 3
    }
}
